package zdream.pmfield.stage.effect;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

import zdream.pmfield.data.foe.MoveAdditionalEffects;
import zdream.pmfield.stage.buff.foe.ChargingBuff;
import zdream.pmfield.stage.buff.foe.FoeStageBuff;
import zdream.pmfield.stage.buff.foe.MoveExecutingBuff;
import zdream.pmfield.stage.director.AttackContext;
import zdream.pmfield.stage.director.HookStore;
import zdream.pmfield.stage.director.HookType;
import zdream.pmfield.stage.director.Hooks;
import zdream.pmfield.stage.message.FoeAddBuffMessage;
import zdream.pmfield.stage.object.FoeActor;

import static zdream.pmfield.data.foe.MoveAdditionalEffects.E_CHARGING;

/**
 * @author Zdream
 * @date 2023-12-19
 * @since 0.0.1
 */
public class ChargingEffect extends AbstractEffect {
	/**
	 * 分别是选择时间点到下个轮次的时间, 以及技能释放到现在的时间 (全按倍率)
	 * 1 表示等于 {@link FoeActor#speedCost}
	 */
	float durationForTurn, durationForRelease;
	int bonusRollPerActionPoints, bonusRollPerMovePoints;

	public ChargingEffect(MoveAdditionalEffects.EffectDescription description) {
		super(E_CHARGING);

		final Map<String, ?> parsed = MoveAdditionalEffects.parse(description);
		Objects.requireNonNull(parsed);
		durationForTurn = getFloat(parsed, "turn", 1);
		durationForRelease = getFloat(parsed, "release", 0.5f);
		bonusRollPerActionPoints = getInt(parsed, "actionPointRoll", 0);
		bonusRollPerMovePoints = getInt(parsed, "movePointRoll", 0);
	}

	private float getFloat(Map<String, ?> map, String key, float defaultValue) {
		final Object v = map.get(key);
		return v == null ? defaultValue : Float.parseFloat(v.toString());
	}

	private int getInt(Map<String, ?> map, String key, int defaultValue) {
		final Object v = map.get(key);
		return v == null ? defaultValue : Integer.parseInt(v.toString());
	}

	@Override
	public void init(Function<HookType, HookStore.HookHandlerBuilder> onHook) {
		onHook.apply(HookType.ON_CHECK_TYPE_FIXED)
				.withPriority(700)
				.handle(this::applyBuffAfterAttack)
				.buildAndSave();
		onHook.apply(HookType.AFTER_EXECUTE_ATTACK)
				.handle(this::updateFoeTurnDuration)
				.buildAndSave();
	}

	private void applyBuffAfterAttack(Hooks.HookContext rawContext) {
		Hooks.AttackHookContext hooks = rawContext.as();
		List<FoeActor> foes = affectedFoesWithTarget(hooks.attackContext, EffectTarget.self);
		for (FoeActor foe : foes) {
			final FoeStageBuff buff = createChargingBuff(foe, hooks.attackContext);
			final FoeActor.ReceiveBuffReport report = foe.receiveBuff(buff);
			pushMessage(foe.stage, new FoeAddBuffMessage(report));
		}
	}

	private FoeStageBuff createChargingBuff(FoeActor foe, AttackContext attackContext) {
		MoveExecutingBuff executingBuff = foe.findMoveExecutingBuff();

		int rollBonus = bonusRollPerActionPoints * attackContext.actionPoints;
		rollBonus += bonusRollPerMovePoints * attackContext.movePoints;

		return new ChargingBuff(foe, executingBuff.move)
				.withTargetSeat(Objects.requireNonNull(attackContext.action.targetSeat))
				.withReleaseDurationRate(durationForRelease)
				.withRollBonus(rollBonus);
	}

	private void updateFoeTurnDuration(Hooks.HookContext rawContext) {
		Hooks.AttackHookContext hooks = rawContext.as();
		hooks.attackContext.speedCostExtraRate += (durationForTurn - 1);
	}
}
